package com.yuyaogc.lowcode.engine.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
import org.apache.commons.lang3.StringUtils;

import java.util.Iterator;

public class JsonObjectUtils {

    public static String combine(String baseJson, String extJson) {
        ObjectMapper mapper = new ObjectMapper();
        Configuration config = Configuration.builder().mappingProvider(new JacksonMappingProvider(mapper))
                .jsonProvider(new JacksonJsonNodeJsonProvider(mapper)).build();

        DocumentContext docContext = null;
        JsonNode extRootNode = null;
        try {
            extRootNode = mapper.readTree(extJson);
            docContext = JsonPath.using(config).parse(mapper.readTree(baseJson).toString());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return baseJson;
        }

        JsonNode jsonPathArrayNode = extRootNode.get("jsonpath");
        for (int k = 0; k < jsonPathArrayNode.size(); k++) {
            boolean findPath = true;
            JsonNode jsonPathNode = jsonPathArrayNode.get(k);
            String expr = jsonPathNode.get("expr").asText();
            String has = null;
            String hasNot = null;
            String parentExpr = expr;
            String position = "inside";

            if (jsonPathNode.has("has_not")) {
                hasNot = jsonPathNode.get("has_not").asText();
            }
            if (jsonPathNode.get("position") != null) {
                position = jsonPathNode.get("position").asText();
            }

            ArrayNode changeNode = (ArrayNode) jsonPathNode.get("body");
            String column = null;
            String tabName = null;
            if (StringUtils.startsWith(expr, "columns") || StringUtils.startsWith(expr, "buttons")
                    || StringUtils.startsWith(expr, "tbar")) {
                parentExpr = String.format("$.%s", StringUtils.substringBefore(expr, "."));
                column = StringUtils.substringAfterLast(expr, ".");
            } else if (StringUtils.startsWith(expr, "tabs[")) {
                if (StringUtils.contains(expr, "columns.")) {
                    parentExpr = String.format("$.%s", StringUtils.substringBeforeLast(expr, "."));
                    column = StringUtils.substringAfterLast(expr, "columns.");
                } else {
                    if (StringUtils.contains(expr, "@.name")) {
                        parentExpr = String.format("$.%s", StringUtils.substringBefore(expr, "["));
                        tabName = StringUtils.substringBetween(expr, "@.name=='", "'");
                    }
                }

            } else if (StringUtils.startsWith(expr, "tabs") || StringUtils.startsWith(expr, "card")) {
                parentExpr = String.format("$.%s", StringUtils.substringBefore(expr, "."));
            }

            JsonNode findNode = null;
            try {
                findNode = docContext.read(parentExpr);
            } catch (Exception e) {
                if (StringUtils.isNotBlank(hasNot) && (findNode == null)) {
                    findPath = false;
                    parentExpr = StringUtils.substringBeforeLast(parentExpr, ".");
                    findNode = docContext.read(parentExpr);
                } else {
                    continue;
                }

            }

            // 如果节点不存在,则创建节点
            if (findPath == false) {
                JsonNode createNode = buildNode(hasNot, parentExpr, docContext, changeNode);
                docContext.put(parentExpr, hasNot, createNode);
                return docContext.jsonString();
            }

            if (findNode.isEmpty()) {
                continue;
            }

            if ("after".equalsIgnoreCase(position)) {
                JsonNode firstNode = update(position, findNode, column, tabName, changeNode, has, hasNot);
                if (firstNode != null) {
                    docContext.set(parentExpr, firstNode);
                }

            } else if ("before".equalsIgnoreCase(position)) {
                JsonNode firstNode = update(position, findNode, column, tabName, changeNode, has, hasNot);
                if (firstNode != null) {
                    docContext.set(parentExpr, firstNode);
                }
            } else if ("replace".equalsIgnoreCase(position)) {
                JsonNode firstNode = update(position, findNode, column, tabName, changeNode, has, hasNot);
                if (firstNode != null) {
                    docContext.set(parentExpr, firstNode);
                }

            } else if ("attributes".equalsIgnoreCase(position)) {
                if (changeNode != null && changeNode.size() > 0) {
                    docContext.set(parentExpr, changeNode.get(0).asText());
                }

            } else {// inside
                JsonNode firstNode = update(position, findNode, column, tabName, changeNode, has, hasNot);
                if (firstNode != null) {
                    docContext.set(parentExpr, firstNode);
                }
            }

        }

        return docContext.jsonString();

    }

    private static JsonNode buildNode(String hasNot, String parentPath, DocumentContext docContext, JsonNode changeNode) {
        if (StringUtils.isNotBlank(hasNot)) {
            ObjectMapper mapper = new ObjectMapper();
            ArrayNode arrayNode = mapper.createArrayNode();
            for (Iterator<JsonNode> it = changeNode.iterator(); it.hasNext(); ) {
                JsonNode valueNode = it.next();
                arrayNode.add(valueNode);
            }

            return arrayNode;
        }
        return null;
    }
    private static JsonNode update(String position, JsonNode findNode, String column, String tabName, JsonNode changeNode,
                                   String has, String hasNot) {
        JsonNode updateNode = null;
        if (!findNode.isArray()) {
            return updateNode;
        }
        ArrayNode parentNode = null;
        JsonNode firstNode = findNode.get(0);
        if (firstNode.isArray()) {
            parentNode = (ArrayNode) firstNode;
        } else {
            if ("replace".equals(position)) {
                findNode = ((ArrayNode) findNode).removeAll();
            }
            parentNode = (ArrayNode) findNode;
        }
        int columnIndex = getColumnIndex(parentNode, column);
        if (StringUtils.isBlank(column)) {
            columnIndex = parentNode.size();
        }

        if (StringUtils.isNotBlank(tabName)) {
            columnIndex = getTabIndex(parentNode, tabName);
            if (columnIndex == -1) {
                columnIndex = parentNode.size();
            }
        }

        if (columnIndex == -1) {
            return updateNode;
        }

        int i = 0;
        if ("after".equalsIgnoreCase(position)) {
            i = 1;
        } else if ("before".equalsIgnoreCase(position)) {
            i = 0;
        } else if ("replace".equalsIgnoreCase(position)) {
            ((ArrayNode) findNode).remove(columnIndex);
        } else if ("inside".equalsIgnoreCase(position)) {
            i = 0;
        }

        for (Iterator<JsonNode> it = changeNode.iterator(); it.hasNext(); ) {
            JsonNode valueNode = it.next();
            parentNode.insert(columnIndex + i, valueNode);
            i++;
        }
        return parentNode;

    }

    private static int getColumnIndex(ArrayNode arrayNode, String column) {
        for (int i = 0; i < arrayNode.size(); i++) {
            String fieldName = null;
            JsonNode columnNode = arrayNode.get(i);
            if (columnNode.isValueNode()) {
                fieldName = columnNode.asText();
            } else if (columnNode.isObject()) {
                ObjectNode columnObjNode = (ObjectNode)columnNode;
                JsonNode columnNameNode = columnObjNode.get("name");
                if (columnNameNode != null) {
                    fieldName = columnNameNode.asText();
                }
            }

            if (StringUtils.equals(column, fieldName)) {
                return i;
            }
        }
        return -1;
    }

    private static int getTabIndex(ArrayNode arrayNode, String tabName) {
        for (int i = 0; i < arrayNode.size(); i++) {
            JsonNode tabNode = arrayNode.get(i);
            if (tabNode != null && tabNode.has("name")) {
                if (StringUtils.equals(tabName, tabNode.get("name").asText())) {
                    return i;
                }
            }
        }
        return -1;
    }

  
}
